package com.xzc.one.common.util;

import cn.hutool.core.convert.Convert;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 对象与map的转化工具类
 *
 * @author xiongzhicong
 * @create 2017-10-31 11:21
 **/
public class ObjectMapUtil {

    static class User {
        private String username;
        private String password;
        private Integer genderStatus;
        private LocalDateTime localDateTime;

        public void setUsername(String username) {
            this.username = username;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public void setGenderStatus(Integer genderStatus) {
            this.genderStatus = genderStatus;
        }

        public void setLocalDateTime(LocalDateTime localDateTime) {
            this.localDateTime = localDateTime;
        }

        @Override
        public String toString() {
            return "User{" +
                    "username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", genderStatus=" + genderStatus +
                    ", localDateTime=" + localDateTime +
                    '}';
        }
    }

    public static Field findField(Class<?> clazz, String name) {
        try {
            return clazz.getField(name);
        } catch (NoSuchFieldException e) {
            return findDeclaredField(clazz, name);
        }
    }

    public static Field findDeclaredField(Class<?> clazz, String name) {
        try {
            return clazz.getDeclaredField(name);
        } catch (NoSuchFieldException e) {
            if (clazz.getSuperclass() != null) {
                return findDeclaredField(clazz.getSuperclass(), name);
            }
            return null;
        }
    }

    public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
        try {
            return clazz.getMethod(methodName, paramTypes);
        } catch (NoSuchMethodException e) {
            return findDeclaredMethod(clazz, methodName, paramTypes);
        }
    }

    public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
        try {
            return clazz.getDeclaredMethod(methodName, paramTypes);
        } catch (NoSuchMethodException e) {
            if (clazz.getSuperclass() != null) {
                return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
            }
            return null;
        }
    }

    public static Object getProperty(Object obj, String name) {
        Object value = null;
        Field field = findField(obj.getClass(), name);
        if (field == null) {
            System.err.println("no such field [" + name + "]");
        }
        boolean accessible = field.isAccessible();
        field.setAccessible(true);
        try {
            value = field.get(obj);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        field.setAccessible(accessible);
        return value;
    }

    public static void setProperty(Object obj, String name, Object value) {
        Field field = findField(obj.getClass(), name);
        if (field == null) {
            System.err.println("no such field [" + name + "]");
        }
        boolean accessible = field.isAccessible();
        field.setAccessible(true);
        try {
            field.set(obj, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        field.setAccessible(accessible);
    }

    public static Map<String, Object> obj2Map(Object obj) {
        HashMap<String, Object> map = new HashMap<>();
        if (obj != null) {
            try {
                Class<?> clazz = obj.getClass();
                do {
                    Field[] fields = clazz.getDeclaredFields();
                    for (Field field : fields) {
                        int mod = field.getModifiers();
                        if (Modifier.isStatic(mod)) {
                            continue;
                        }
                        boolean accessible = field.isAccessible();
                        field.setAccessible(true);
                        map.put(field.getName(), field.get(obj));
                        field.setAccessible(accessible);
                    }
                    clazz = clazz.getSuperclass();
                } while (clazz != null);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    public static Map<String, Object> obj2Map(Object... obj) {
        Map<String, Object> map = new HashMap<>();
        for (Object o : obj) {
            map.putAll(obj2Map(o));
        }
        return map;
    }

    public static <T> T map2Obj(Map<String, Object> map, Class<T> tClass) {
        Field[] fields = tClass.getDeclaredFields();
        T obj = null;
        try {
            obj = tClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        for (String key : map.keySet()) {
            Field field = Arrays.stream(fields).filter(r -> r.getName().equals(key)).findFirst().orElse(null);
            if (field != null) {
                try {
                    field.setAccessible(true);
                    field.set(obj, Convert.convert(field.getType(), map.get(key)));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return obj;
    }

    public static <T> T map2Obj(Map<String, Object> map, T obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (String key : map.keySet()) {
            Field field = Arrays.stream(fields).filter(r -> r.getName().equals(key)).findFirst().orElse(null);
            if (field != null) {
                try {
                    field.setAccessible(true);
                    field.set(obj, Convert.convert(field.getType(), map.get(key)));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return obj;
    }

    /**
     * 获得父类集合，包含当前class
     *
     * @param clazz 类
     * @return List<Class < ?>>
     */
    public static List<Class<?>> getSuperclassList(Class<?> clazz) {
        List<Class<?>> clazzes = new ArrayList<>(3);
        clazzes.add(clazz);
        clazz = clazz.getSuperclass();
        while (clazz != null) {
            clazzes.add(clazz);
            clazz = clazz.getSuperclass();
        }
        return Collections.unmodifiableList(clazzes);
    }

    /**
     * 比较bean 不同字段的值
     *
     * @param pre   原始对象
     * @param after 改之后的对象
     * @param <T>   泛型
     * @return String
     */
    public static <T> String compareBean(T pre, T after) {
        Field[] fields = pre.getClass().getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        for (Field f : fields) {
            f.setAccessible(true);
            try {
                Object preV = f.get(pre);
                Object aftV = f.get(after);
                if (aftV != null && !aftV.equals(preV)) {
                    sb.append("字段：" + f.getName() + "由[" + preV + "]改为[" + aftV + "]\n");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

    public static void main(String... args) {
        User user = new User();
        user.setUsername("aaa");
        user.setPassword("bbb");
        user.setGenderStatus(89);
        user.setLocalDateTime(LocalDateTime.now());
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("username", "123456");
        hashMap.put("password", "1.1");
        hashMap.put("genderStatus", 1);
        hashMap.put("localDateTime", LocalDateTime.now());
        //对象转map
        Map<String, Object> map = obj2Map(user);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println(entry.getKey() + entry.getValue());
        }
        User user1 = (User) map2Obj(hashMap, User.class);
        //    User user2 = (User) map2Obj(hashMap, user);
        System.out.println(user1.toString());
        Map<String, Object> map1 = obj2Map(user, user1);
        for (Map.Entry<String, Object> entry : map1.entrySet()) {
            System.out.println(entry.getKey() + entry.getValue());
        }
        System.out.println(user + user1.toString());
        System.out.println(compareBean(user, user1));
        System.out.println(getProperty(user, "username"));
    }
}